/**
 * 验证手机号码是否合法
 * @param {string} mobile - 需要验证的手机号码
 * @param {boolean} showToast - 是否在验证失败时显示提示，默认为true
 * @param {string} message - 自定义提示消息，默认为"手机号码不合法，请重新输入"
 * @return {boolean} - 返回验证结果
 */
export const isTelephone = (mobile, showToast = true, message = "手机号码不合法，请重新输入") => {
	// 如果手机号为空，直接返回false
	if (!mobile) {
		showToast && toast(message);
		return false;
	}
	
	// 匹配中国大陆手机号（13-19号段）
	const mobileRegex = /^1[3-9]\d{9}$/;
	const isValid = mobileRegex.test(mobile);
	
	if (!isValid && showToast) {
		toast(message);
	}
	return isValid;
}

// 为了保持向后兼容，添加一个导出checkPhone函数，调用isTelephone
export const checkPhone = (phone) => {
  return isTelephone(phone, false);
};
// 时间戳转换时间年月日 时分秒: 2025-04-09 10:00:00
export const timestampToDate = (timestamp) => {
  return new Date(timestamp * 1000).toLocaleString();
};

/**
 * 将时间戳格式化为"年-月-日 时:分:秒"格式
 * @param {number|string} timestamp - 时间戳（秒或毫秒）
 * @param {boolean} isMillisecond - 是否为毫秒级时间戳，默认为false（秒级时间戳）
 * @return {string} - 格式化后的日期字符串
 */
export const formatDateTime = (timestamp, isMillisecond = false) => {
  if (!timestamp) return '';
  
  // 转换为毫秒级时间戳
  const date = new Date(isMillisecond ? Number(timestamp) : Number(timestamp) * 1000);
  
  // 获取年月日时分秒
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');
  
  // 格式化为"年-月-日 时:分:秒"
  return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};

/**
 * 将时间戳格式化为"年-月-日"格式
 * @param {number|string} timestamp - 时间戳（秒或毫秒）
 * @param {boolean} isMillisecond - 是否为毫秒级时间戳，默认为false（秒级时间戳）
 * @return {string} - 格式化后的日期字符串
 */
export const formatDate = (timestamp, isMillisecond = false) => {
  if (!timestamp) return '';
  
  // 转换为毫秒级时间戳
  const date = new Date(isMillisecond ? Number(timestamp) : Number(timestamp) * 1000);
  
  // 获取年月日
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  
  // 格式化为"年-月-日"
  return `${year}-${month}-${day}`;
};

/**
 * 将时间戳格式化为"时:分:秒"格式
 * @param {number|string} timestamp - 时间戳（秒或毫秒）
 * @param {boolean} isMillisecond - 是否为毫秒级时间戳，默认为false（秒级时间戳）
 * @return {string} - 格式化后的时间字符串
 */
export const formatTime = (timestamp, isMillisecond = false) => {
  if (!timestamp) return '';
  
  // 转换为毫秒级时间戳
  const date = new Date(isMillisecond ? Number(timestamp) : Number(timestamp) * 1000);
  
  // 获取时分秒
  const hours = String(date.getHours()).padStart(2, '0');
  const minutes = String(date.getMinutes()).padStart(2, '0');
  const seconds = String(date.getSeconds()).padStart(2, '0');
  
  // 格式化为"时:分:秒"
  return `${hours}:${minutes}:${seconds}`;
};

/**
 * 自定义日期时间格式化
 * @param {number|string} timestamp - 时间戳（秒或毫秒）
 * @param {string} format - 格式化模板，如 'YYYY-MM-DD HH:mm:ss'
 * @param {boolean} isMillisecond - 是否为毫秒级时间戳，默认为false（秒级时间戳）
 * @return {string} - 格式化后的日期时间字符串
 */
export const formatDateByPattern = (timestamp, format = 'YYYY-MM-DD HH:mm', isMillisecond = false) => {
  if (!timestamp) return '';
  
  // 转换为毫秒级时间戳
  const date = new Date(isMillisecond ? Number(timestamp) : Number(timestamp) * 1000);
  
  // 定义格式化对象
  const formatObj = {
    YYYY: date.getFullYear(),
    MM: String(date.getMonth() + 1).padStart(2, '0'),
    DD: String(date.getDate()).padStart(2, '0'),
    HH: String(date.getHours()).padStart(2, '0'),
    mm: String(date.getMinutes()).padStart(2, '0'),
    ss: String(date.getSeconds()).padStart(2, '0')
  };
  
  // 替换格式字符串
  return format.replace(/(YYYY|MM|DD|HH|mm|ss)/g, match => formatObj[match]);
};

// 手机号脱敏
export const desensitizePhone = (phone) => {
  return phone && phone.replace(/(\d{3})(\d{4})(\d{4})/, '$1****$3');
};

// 身份证号脱敏
export const desensitizeIdCard = (idCard) => {
  return idCard.replace(/(\d{6})(\d{4})(\d{4})(\d{4})(\d{4})/, '$1**********$5');
};

/**
 * @param {*} fn 要执行的函数
 * @param {*} delay 延迟时间
 * @param {*} immediate 是否立即执行
 * @returns 
 */
export function debounce(fn, delay, immediate = false) {
	// 1.定义一个定时器, 保存上一次的定时器
	let timer = null;
	let isInvoke = false; //记录立即执行是否已执行过

	// 2.真正执行的函数
	const _debounce = function(...args) {
		// 取消上一次的定时器
		if (timer) clearTimeout(timer);

		// 判断是否需要立即执行
		if (immediate && !isInvoke) {
			fn.apply(this, args);
			isInvoke = true;
		} else {
			// 延迟执行
			timer = setTimeout(() => {
				// 外部传入的真正要执行的函数
				fn.apply(this, args);

				//没有这个步骤时，只有第一次输入是立即执行，即使后面延迟执行后再输入也是延迟执行；
				// 有这个步骤时，第一次输入时立即执行，后面延迟执行后再输入也会有立即执行
				isInvoke = false
				timer = null
			}, delay);
		}
	};

	// 封装取消功能
	_debounce.cancel = function() {
		if (timer) clearTimeout(timer)
		timer = null //重置
		isInvoke = false //重置
	}

	return _debounce;
}

export function throttle(fn, delay) {
	let timer = null;
	return function() {
		let context = this;
		let args = arguments;
		if (!timer) {
			timer = setTimeout(function() {
				fn.apply(context, args);
				timer = null;
			}, delay);
		}
	};
}


/**
 * 节流函数
 * @param {Function} fn - 需要节流的函数
 * @param {number} delay - 延迟时间（毫秒）
 * @param {Object} options - 配置选项
 * @param {boolean} options.leading - 是否在延迟开始前调用函数，默认为 true
 * @param {boolean} options.trailing - 是否在延迟结束后调用函数，默认为 true
 * @returns {Function} - 节流后的函数
 */
export function throttleFn(fn, delay, options = {}) {
    // 参数检查
    if (typeof fn !== 'function') {
        throw new TypeError('Expected a function');
    }
    if (typeof delay !== 'number' || delay < 0) {
        throw new TypeError('Expected delay to be a positive number');
    }

    const {
        leading = true,    // 是否在延迟开始前调用
        trailing = true    // 是否在延迟结束后调用
    } = options;

    let timer = null;              // 定时器
    let lastExecTime = 0;         // 上次执行时间
    let lastArgs = null;          // 最后一次调用的参数
    let lastThis = null;          // 最后一次调用的this上下文

    // 实际执行函数
    function execute() {
        const now = Date.now();
        
        if (lastArgs) {
            fn.apply(lastThis, lastArgs);
            lastArgs = lastThis = null;
            lastExecTime = now;
        }
    }

    // 清理函数
    function cleanup() {
        if (timer) {
            clearTimeout(timer);
            timer = null;
        }
    }

    // 返回节流后的函数
    const throttled = function(...args) {
        const now = Date.now();
        const remaining = delay - (now - lastExecTime);

        // 保存上下文和参数
        lastArgs = args;
        lastThis = this;

        // 首次调用且配置了leading
        if (lastExecTime === 0 && !leading) {
            lastExecTime = now;
            return;
        }

        // 检查是否可以立即执行
        if (remaining <= 0) {
            cleanup();
            execute();
        } 
        // 设置延迟调用
        else if (!timer && trailing) {
            timer = setTimeout(() => {
                execute();
                cleanup();
            }, remaining);
        }
    };

    // 添加取消方法
    throttled.cancel = function() {
        cleanup();
        lastExecTime = 0;
        lastArgs = lastThis = null;
    };

    // 添加立即执行方法
    throttled.flush = function() {
        if (timer) {
            execute();
            cleanup();
        }
    };

    return throttled;
}

/**
 * 显示消息提示框
 * @param content 提示的标题
 * @param icon 图标类型，默认为'none'
 * @param duration 提示持续时间，默认为1500ms
 */
export function toast(content, icon = 'none', duration = 1500) {
	uni.showToast({
		icon: icon,
		title: content,
		duration: duration
	})
}

/**
 * 显示模态弹窗
 * @param content 提示的标题
 */
export function showConfirm(content) {
	return new Promise((resolve, reject) => {
		uni.showModal({
			title: '提示',
			content: content,
			cancelText: '取消',
			confirmText:  '确定',
			success: function(res) {
				resolve(res)
			}
		})
	})
}

/**
 * 参数处理
 * @param params 参数
 */
export function tansParams(params) {
	let result = ''
	for (const propName of Object.keys(params)) {
		const value = params[propName]
		var part = encodeURIComponent(propName) + "="
		if (value !== null && value !== "" && typeof(value) !== "undefined") {
			if (typeof value === 'object') {
				for (const key of Object.keys(value)) {
					if (value[key] !== null && value[key] !== "" && typeof(value[key]) !== 'undefined') {
						let params = propName + '[' + key + ']'
						var subPart = encodeURIComponent(params) + "="
						result += subPart + encodeURIComponent(value[key]) + "&"
					}
				}
			} else {
				result += part + encodeURIComponent(value) + "&"
			}
		}
	}
	return result
}
/**
 * 根据显示方式和显示数量处理数据列表
 * @param {Array} dataList - 原始数据列表
 * @param {string} displayMode - 显示方式，可选值：'all'(全部显示)、'sequence'(顺序显示)、'random'(随机显示)
 * @param {number} displayCount - 显示数量，0表示显示全部
 * @param {number} startIndex - 顺序显示时的起始索引，默认为0（仅在 'sequence' 模式下有效）
 * @return {Array} - 处理后的数据列表
 * 
 * @example
 * // 原始数据列表
 * const dataList = [
 *   { id: 1, name: '项目1' },
 *   { id: 2, name: '项目2' },
 *   { id: 3, name: '项目3' },
 *   { id: 4, name: '项目4' },
 *   { id: 5, name: '项目5' },
 *   { id: 6, name: '项目6' },
 *   { id: 7, name: '项目7' },
 *   { id: 8, name: '项目8' },
 *   { id: 9, name: '项目9' }
 * ];
 * 
 * // 全部显示 (返回所有9条数据)
 * const allData = processDisplayList(dataList, '0', 0);
 * 
 * // 全部显示，但限制数量为3条 (返回前3条数据)
 * const limitedData = processDisplayList(dataList, '0', 3);
 * 
 * // 顺序显示，从索引2开始，显示4条数据 (返回索引2-5的数据)
 * const sequenceData = processDisplayList(dataList, '1', 4, 2);
 * 
 * // 随机显示5条数据 (随机返回5条数据)
 * const randomData = processDisplayList(dataList, '2', 5);
 */
export function processDisplayList(dataList, displayMode = '0', displayCount = 0, startIndex = 0) {
	console.log('processDisplayList', dataList, displayMode, displayCount, startIndex)
	// 如果数据列表为空或无效，直接返回空数组
	if (!dataList || !Array.isArray(dataList) || dataList.length === 0) {
	  return { resultList: [], startIndex: 0 };
	}
	
	// 创建数据列表的副本，避免修改原始数据
	let resultList = [...dataList];
	let newStartIndex = startIndex; // 记录下一次的起始索引
	
	// 根据显示方式处理数据
	switch (displayMode) {
	  case '0': // 全部显示，保持原顺序
		break;
		
	  case '1': // 顺序显示，从指定索引开始按顺序显示
		// 确保起始索引有效
		startIndex = Math.min(Math.max(0, startIndex), resultList.length - 1);
		
		// 如果需要显示的数量超过了从起始索引开始的剩余数量，则循环回到开头
		if (displayCount > 0 && startIndex + displayCount > resultList.length) {
		  // 从起始索引到末尾的部分
		  const firstPart = resultList.slice(startIndex);
		  // 从开头到补齐所需数量的部分
		  const remainingCount = displayCount - firstPart.length;
		  const secondPart = resultList.slice(0, remainingCount);
		  resultList = [...firstPart, ...secondPart];
		  
		  // 计算下一次的起始索引
		  newStartIndex = remainingCount;
		} else if (displayCount > 0) {
		  // 如果数量不超过剩余数量，直接从起始索引开始截取
		  resultList = resultList.slice(startIndex, startIndex + displayCount);
		  
		  // 计算下一次的起始索引
		  newStartIndex = (startIndex + displayCount) % dataList.length;
		} else {
		  // 如果没有指定显示数量，则显示从起始索引开始的所有数据
		  resultList = resultList.slice(startIndex);
		  
		  // 下一次从头开始
		  newStartIndex = 0;
		}
		break;
		
	  case '2': // 随机显示，随机打乱顺序
		// 使用 Fisher-Yates 洗牌算法打乱数组
		for (let i = resultList.length - 1; i > 0; i--) {
		  const j = Math.floor(Math.random() * (i + 1));
		  [resultList[i], resultList[j]] = [resultList[j], resultList[i]];
		}
		break;
		
	  default:
		console.warn(`未知的显示方式: ${displayMode}，使用默认的'all'模式`);
		break;
	}
	
	// 根据显示数量截取数据（仅对'all'和'random'模式适用，'sequence'模式已在上面处理）
	if (displayMode == '2' && displayCount > 0 && displayCount < resultList.length) {
	  resultList = resultList.slice(0, displayCount);
	}
	return { resultList, startIndex: newStartIndex };
}
/**
 * 将数组按时间戳属性进行分组
 * @param {Array} dataList - 包含时间戳的数据数组
 * @param {Object} options - 配置选项
 * @param {string} options.timeField - 时间戳字段名，默认为'time'
 * @param {number} options.recentDays - 最近天数的界限，默认为30天
 * @param {string} options.recentLabel - 最近日期的标签，默认为'近30天'
 * @param {boolean} options.isMillisecond - 时间戳是否为毫秒级，默认为true
 * @param {boolean} options.showYear - 是否在月份中显示年份，默认为true
 * @return {Object} - 分组后的数据对象，键为分组名称，值为该组的数据数组
 * 
 * @example
 * // 示例数据（假设当前日期是2023年5月15日）
 * const testData = [
 *   { id: 1, title: '数据1', time: Date.now() }, // 今天
 *   { id: 2, title: '数据2', time: Date.now() - 5 * 24 * 60 * 60 * 1000 }, // 5天前
 *   { id: 3, title: '数据3', time: Date.now() - 40 * 24 * 60 * 60 * 1000 }, // 40天前(4月)
 *   { id: 4, title: '数据4', time: Date.now() - 70 * 24 * 60 * 60 * 1000 }, // 70天前(3月)
 *   { id: 5, title: '数据5', time: Date.now() - 400 * 24 * 60 * 60 * 1000 }, // 去年数据
 * ];
 * 
 * // 基本用法
 * const grouped = groupByTime(testData);
 * // 结果大致为：{ '近30天': [数据1, 数据2], '2023年4月': [数据3], '2023年3月': [数据4], '2022年5月': [数据5] }
 * 
 * // 自定义参数
 * const customGrouped = groupByTime(testData, { 
 *   timeField: 'time',
 *   recentDays: 10,
 *   recentLabel: '最近10天',
 *   showYear: false // 不显示年份，只显示月份
 * });
 */
export function groupByTime(dataList, options = {}) {
  // 设置默认选项
  const {
    timeField = 'time',
    recentDays = 30,
    recentLabel = `近${recentDays}天`,
    isMillisecond = false,
    showYear = true
  } = options;
  
  // 检查输入数据的有效性
  if (!Array.isArray(dataList) || dataList.length === 0) {
    return {};
  }
  
  const today = new Date();
  const recentDate = new Date(today.getTime() - recentDays * 24 * 60 * 60 * 1000);
  const groupedData = {};
  
  // 获取月份名称和年份（可配置是否显示年份）
  const getMonthLabel = (date) => {
    // 使用阿拉伯数字获取月份（1-12）
    const month = date.getMonth() + 1; // getMonth()返回0-11
    if (showYear) {
      const year = date.getFullYear();
      return `${year}年${month}月`;
    }
    return `${month}月`;
  };
  
  dataList.forEach(item => {
    // 确保时间戳字段存在
    if (item[timeField] === undefined) {
      console.warn(`项目缺少时间字段: ${timeField}`, item);
      return; // 跳过此项目
    }
    
    // 根据是否为毫秒级时间戳进行转换
    const timestamp = isMillisecond ? 
      Number(item[timeField]) : 
      Number(item[timeField]) * 1000;
    // 创建日期对象
    const itemDate = new Date(timestamp);
    // 如果日期无效，跳过此项目
    if (isNaN(itemDate.getTime())) {
      console.warn(`无效的时间戳值: ${item[timeField]}`, item);
      return;
    }
    
    // 确定分组键
    let groupKey;
    if (itemDate >= recentDate) {
      groupKey = recentLabel;
    } else {
      groupKey = getMonthLabel(itemDate);
    }
    
    // 创建分组（如果不存在）
    if (!groupedData[groupKey]) {
      groupedData[groupKey] = [];
    }
    
    // 将项目添加到适当的分组
    groupedData[groupKey].push(item);
  });
  
  return groupedData;
}









